模块:I18n
-- i18n module local i18n = {} -- Module variables & dependencies. local title = mw.title.getCurrentTitle() local fallbacks = require('Module:Fallbacklist') -- getUserLang function i18n.getUserLang() local langs = { -- "zh", "zh-hans", -- "zh-cn", -- "zh-sg", -- "zh-my", "zh-hant", -- "zh-tw", -- "zh-hk", -- "zh-mo", "en", "aa", "ab", "ace", "af", "ak", "aln", "am", "anp", "ar", "arc", "arn", "ary", "arz", "as", "av", "avk", "ay", "az", "ba", "bat-smg", "bcc", "bcl", "be", "be-tarask", "be-x-old", "bg", "bh", "bho", "bi", "bjn", "bm", "bn", "bo", "bpy", "bqi", "br", "brh", "bs", "bug", "bxr", "ca", "cbk-zam", "cdo", "ce", "ceb", "ch", "cho", "chr", "chy", "ckb", "co", "cps", "cr", "crh", "crh-cyrl", "crh-latn", "cs", "csb", "cu", "cv", "cy", "da", "de", "diq", "dsh", "dtp", "dv", "dz", "ee", "el", "eml", "eo", "es", "et", "eu", "ext", "fa", "ff", "fi", "fiu-vro", "fj", "fo", "fr", "frp", "frr", "fur", "fy", "ga", "gag", -- "gan", "gan-hans", "gan-hant", "gd", "gl", "glk", "gn", "got", "grc", "gsw", "gu", "gv", "ha", "hak", "haw", "he", "hi", "hif", "hif-latn", "hil", "ho", "hr", "hsb", "ht", "hu", "hy", "hz", "id", "ig", "ii", "ik", "ike-cans", "ike-latn", "ilo", "inh", "io", "is", "it", "iu", "ja", "jam", "jbo", "jut", "jv", "ka", "kaa", "kab", "kbd", "kbd-cyrl", "kg", "khw", "ki", "kiu", "kj", "kk", "kk-arab", "kk-cn", "kk-cyrl", "kk-kz", "kk-latn", "kk-tr", "kl", "km", "kn", "ko", "ko-kp", "koi", "kr", "krc", "kri", "krj", "ks", "ks-arab", "ks-deva", "ku", "ku-arab", "ku-latn", "kv", "kw", "ky", "la", "lad", "lb", "lbe", "lez", "lfn", "lg", "li", "lij", "liv", "lmo", "ln", "lo", "loz", "lt", "ltg", "lv", "lzh", "lzz", "mai", "map-bms", "mdf", "mg", "mh", "mhr", "mi", "min", "mk", "ml", "mn", "mo", "mr", "mrj", "ms", "mt", "mus", "my", "myv", "mzn", "na", "nah", "nan", "nap", "ne", "new", "ng", "niu", "nl", "nl-informal", "nn", "no", "nov", "nrm", "nso", "nv", "ny", "oc", "om", "or", "os", "pa", "pag", "pam", "pap", "pcd", "pi", "pih", "pl", "pms", "pnb", "pnt", "prg", "ps", "pt", "pt-br", "qu", "qug", "rgn", "rif", "rm", "rmy", "rn", "ro", "roa-rup", "roa-tara", "ru", "rue", "rup", "ruq", "ruq-cyrl", "ruq-latn", "rw", "sa", "sah", "sc", "scn", "sco", "sd", "sdc", "se", "sei", "sg", "sgs", "sh", "shi", "shi-latn", "shi-tfng", "si", "sk", "sl", "sli", "sm", "sma", "sn", "so", "sq", "sr", "sr-ec", "sr-el", "srn", "ss", "st", "stq", "su", "sv", "sw", "szl", "ta", "tcy", "te", "tet", "tg", "tg-cyrl", "tg-latn", "th", "ti", "tk", "tl", "tly", "tn", "to", "tpi", "tr", "ts", "tt", "tt-cyrl", "tt-latn", "tum", "tw", "ty", "tyv", "udm", "ug", "ug-arab", "ug-latn", "uk", "ur", "uz", "val", "ve", "vec", "vep", "vi", "vls", "vmf", "vo", "vot", "vro", "wa", "war", "wo", "wuu", "xal", "xh", "xmf", "yi", "yo", "yue", "za", "zea", "zu" } local l = 1 local lcode = mw.language.getContentLanguage():getCode() local langmsgcurr = frame:preprocess( ' ' ) local langmsg = frame:preprocess( ' ' ) while( langmsg ~= langmsgcurr and langsl ~= '' and langsl ~= nil ) do l = l + 1 langmsg = frame:preprocess( ' ' ) end if langsl '' or langsl nil then lcode = mw.language.getContentLanguage():getCode() else lcode = langsl end lcode = tostring( lcode ) return lcode end -- I18n datastore class. -- @section i18nd local i18nd = {} -- Datastore language getter. -- @name i18nd:getLang -- @return {string} Default language (datastore messages). function i18nd:getLang() return self.defaultLang end -- Datastore language setter to user language. -- @name i18nd:useUserLang -- @return {self} Self instance. function i18nd:useUserLang() self.defaultLang = i18n.getLang() or self.defaultLang return self end -- Datastore language setter to user language. -- @name i18nd:useContentLang -- @return {self} Self instance. function i18nd:useContentLang() self.defaultLang = mw.language.getContentLanguage():getCode() return self end -- Datastore language setter to specificed language. -- @name i18nd:useLang -- @param {string} code Language code to use. -- @return {self} Self instance. function i18nd:useLang(code) self.defaultLang = isValidCode(code) and code or self.defaultLang return self end -- Datastore temporary language setter to user language. -- @name i18nd:inUserLang -- @return {self} Self instance. function i18nd:inUserLang() self.tempLang = i18n.getLang() or i18nd.tempLang return self end -- Datastore temporary language setter to user language. -- @name i18nd:inContentLang -- @return {self} Self instance. function i18nd:inContentLang() self.tempLang = mw.language.getContentLanguage():getCode() return self end -- Datastore temporary language setter to specificed language. -- @name i18nd:inLang -- @param {string} code Language code to use. -- @return {self} Self instance. function i18nd:inLang(code) self.tempLang = isValidCode(code) and code or self.tempLang return self end -- Datastore temporary source setter to specificed datastore. -- @name i18nd:fromSource -- @param {string} ... Source name(s) to use. -- @return {self} Self instance. function i18nd:fromSource(...) local c = select('#', ...) if c ~= 0 then self.tempSources = {} for i = 1, c do local n = select(i, ...) if type(n) 'string' and type(self._sourcesn) 'number' then self.tempSourcesn = self._sourcesn end end end return self end -- Datastore message utility. -- @name i18nd:msg -- @param {string|table} opts Message configuration or key. -- @paramopt {string} opts.key Message key to return. -- @paramopt {table} opts.args Arguments to substitute. -- @paramopt {table} opts.sources Source names to limit to. -- @paramopt {table} opts.lang Temporary language to use. -- @paramopt {string} ... Arguments to substitute. -- @return {string} Message key or ''. -- @todo Better fallback system with Module:Fallbacklist. function i18nd:msg(opts, ...) local frame = mw.getCurrentFrame() -- Argument normalization. if not self or not opts then error('missing arguments in i18nd:msg') end local key = type(opts) 'table' and opts.key or opts local args = opts.args or {...} -- Configuration parameters. if opts.sources then self:fromSources(unpack(opts.sources)) end if opts.lang then self:inLang(opts.lang) end -- Source handling. local source_n = self.tempSources or self._sources local source_i = {} for n, i in pairs(source_n) do source_ii = n end self.tempSources = nil -- Language handling. local lang = self.tempLang or self.defaultLang self.tempLang = nil -- Message fetching. local msg for i, messages in ipairs(self._messages) do -- Message data. local msg = (messageslang or {})key -- Fallback support (experimental). for _, l in ipairs((fallbackslang or {})) do if msg nil then msg = (messagesl or {})key end end -- Internal fallback. msg = msg ~= nil and msg or messages.enkey -- Handling argument substitution from Lua. if msg and source_ii and #args > 0 then msg = handleArgs(msg, args) end if msg and source_ii and lang ~= 'qqx' then -- return frame -- and frame:preprocess(mw.text.trim(msg)) -- or mw.text.trim(msg) return frame and frame:preprocess(msg) or msg end end return mw.text.nowiki('<' .. key .. '>') end -- Argument substitution as $n where n > 0. -- @param {string} msg Message to substitute arguments into. -- @param {table} args Arguments table to substitute. -- @return {string} Resulting message. function handleArgs(msg, args) for i, a in ipairs(args) do msg = (string.gsub(msg, '%$' .. i, a)) end return msg end -- Checks whether a language code is valid. -- @param {string} code Language code to check -- @return {bool} Whether the language code is valid. function isValidCode(code) return type(code) 'string' and #mw.language.fetchLanguageName(code) ~= 0 end -- Language code function. -- @usage -- @return {string} code Language code. function i18n.getLang() local code = mw.language.getContentLanguage():getCode() local frame = mw.getCurrentFrame() local subPage = title.subpageText local uselang -- Language argument test. if isValidCode( ( (frame or {}).args or {}).uselang or '') then code = frame.args.uselang elseif isValidCode( ( (frame and frame.getParent(frame) or {}).args or {}).uselang or '') then code = frame:getParent().args.uselang -- Subpage language test. elseif title.isSubpage and isValidCode(subPage) then code = isValidCode(subPage) and subPage or code -- User language test. elseif frame then uselang = frame:preprocess(' ') code = mw.text.decode(uselang) '' and code or uselang end return code end -- I18n message datastore loader. -- @param {string} source ROOTPAGENAME/path of target i18n submodule. -- @usage require('Module:Install').loadMessages(source) -- @raise 'no source supplied to i18n.loadMessages' -- @return {table} i18n I18n datastore instance. function i18n.loadMessages(...) local ds local i = 0 local s = {} for j = 1, select('#', ...) do local source = select(j, ...) if type(source) 'string' and source ~= '' then i = i + 1 ssource = i if not ds then -- Instantiate datastore. ds = {} i18nd.__index = i18nd setmetatable(ds, i18nd) -- Set default language. ds.defaultLang = i18n.getLang() ds._messages = {} end source = string.gsub(source, '^.', mw.ustring.upper) source = mw.ustring.find(source, ':') and source or 'Module:' .. source .. '/i18n' ds._messagesi = mw.loadData(source) end end if not ds then error('no source supplied to i18n.loadMessages') else -- Attach source index map. ds._sources = s -- Return datastore instance. return ds end end -- I18n message function. -- @param {table} frame Frame table from invocation. -- @param {string} frame.args1 ROOTPAGENAME of i18n submodule. -- @param {string} frame.args2 Key of i18n message. -- @param {string} frame.args.lang Default language of message (optional). -- @usage -- @raise 'missing arguments in i18n.getMsg' -- @return {string} msg I18n message in localised language. function i18n.getMsg(frame) if not frame or not frame.args or not frame.args1 or not frame.args2 then error('missing arguments in i18n.getMsg') end local source = frame.args1 local key = frame.args2 -- Pass through extra arguments. local repl = {} for i, a in ipairs(frame.args) do if i >= 3 then repli-2 = a end end -- Load message data. local i18nd = i18n.loadMessages(source) -- Pass through language argument. i18nd:inLang(frame.args.lang) -- Return message. return i18nd:msg { key = key, args = repl } end -- Template wrapper for Template:I18n. -- @param {table} frameChild Frame invocation object. -- @usage function i18n.main(frameChild) local frame = frameChild:getParent() local args = {} for p, v in pairs(frame.args) do argsp = v end -- Extract function name as first argument. local fn_name = args1 and table.remove(args, 1) -- Check for function argument. if fn_name nil then error((mw.ustring.gsub(mw.ustring.match(mw.message.new('scribunto-common-nofunction'):plain(), ':%s(.*)%p$'), '^.', mw.ustring.lower))) end -- Check function exists. if i18nfn_name nil then error((mw.ustring.gsub(mw.ustring.match(mw.message.new('scribunto-common-nosuchfunction'):plain(), ':%s(.*)%p$'), '^.', mw.ustring.lower))) end -- Execute function if it does. frame.args = args return i18nfn_name(frame) end return i18n --